목차

  1. 목차
    1. 개요
    2. 분포수 세기
      1. 분포수 세기의 전략
      2. 분포수 세기의 실제
      3. Java로 구현한 분포수세기
      4. 분포수세기의 분석
    3. 문서에 대하여

개요

  • 같은 키가 많이 있는 배열에 한해 적용할 수 있는 알고리즘이다.
  • 본격적인 정렬 알고리즘이라고 하기엔 다소 한정된 용도에서만 적합하다. O(N) 성능
  • 음이 아닌 양의 정수에서 사용되는 알고리즘이다.

분포수 세기

특정 키값이 출현하는 빈도를 저장하여 누적분포를 이용하여 간단하게 정렬하는 방법이다.

누적분포

  1. 분포 상태를 누적으로 가산하고
  2. 어떤 상태 이하의 확률을 나타낸다. 즉 사상의 확률적인 분포상태를 의미한다.

분포수 세기의 전략

배열을 어떻게 정렬하는 지 본격적으로 알아보자
키 값이 1에서 5까지만의 값을 가지는 a[]라는 정수 배열을 정렬해 보자


1 3 2 2 3 1 3 2 4 2 4 3 1 2 1 2 5 1 5 3

  1. count[] 배열에 각 숫자별 빈도수를 저장한다.

count[1] = 5
count[2] = 6
count[3] = 5
count[4] = 2
count[5] = 2

  1. count[] 배열의 누적 도수를 구한다.

count[1] = 5
count[2] = count[2] + count[1] = 11
count[3] = count[3] + count[2] = 16
count[4] = count[4] + count[3] = 18
count[5] = count[5] + count[4] = 20

    • 누적도수의 의미
      정렬이 완료된 상태에서,
      키 값 1은 0~4 까지
      키 값 2는 5~10까지
      키 값 3은 11~15까지
      키 값 4은 16~17까지
      키 값 5은 18~19까지 차지한다는 것을 의미한다.
  • 분포수 세기 알고리즘을 정리하면 다음과 같다.
    1. count[WEBSTUDY:] 배열을 0으로 초기화
    2. a[] 배열의 키 빈도를 계산하여 count[WEBSTUDY:]에 저장
    3. count[] 배열을 누적분포로 변환
    4. a[] 배열을 뒤에서부터 읽어서 b[--count[a[WEBSTUDY:i]]]에 저장
    5. b[] 배열을 a[]에 복사

분포수 세기의 실제

분포수세기 알고리즘은 안정성(statbility)에 있다. 그래서 a[] 배열에서 값을 꺼내올 때 뒤에서부터 꺼낸다.
그림 참조

Java로 구현한 분포수세기


package com.oracleclub.study.algorithm;

import org.junit.Test;

public class DistributionCountingTest {

	@Test
	public void distributionCounting_test() {
		int[] a = {1, 3, 4, 2, 3};
		int[] b = distributionCounting(a, 5, 4);
		for (int i = 0; i < b.length; i++) {
			System.out.println("b[" + i + "]=" + b[i]);
		}
	}
	
	/**
	 * 
	 * @param a
	 * @param n 입력인자 a[] 배열이 가지는 개수
	 * @param m a[] 배열이 가지는 키 값의 범위
	 */
	private int[] distributionCounting(int a[], int n, int m) {
		int[] b = new int[n];			// 정렬 결과를 담을 변수
		int[] count = new int[m + 1];	//1. count[] 배열 선언 및 초기화
		
		
		// 2. 배열 a[]에서 키의 빈도수를 계산하여 count[]에 저장
		for (int i = 0; i < n; i++) {
			++count[a[i]];
		}
		
		// 3. count[] 배열을 누적분포로 변환
		for (int i = 1; i <= m; i++) {
			count[i] += count[i-1];
		}
		
		// 4. a[] 배열을 뒤에서부터 읽어서 배열 b[--count[a[i]]]에 저장
		for (int i = n - 1; i >= 0; i--) {
			b[--count[a[i]]] = a[i];
		}
		
		return b;
	}	
}

분포수세기의 분석

약 2N번의 비교와 1번의 전체 복사가 있는 정도여서 속도는 빠르다.
하지만, 입력자료의 범위가 아주 넓을 때는 메모리 소모가 너무 커서 아주 느린 성능을 보인다.

  1. 작은 범위의 키 값을 가지는 경우에 사용한다.(키 값은 이산적이어야 한다.연속된 형태가 아니어야 한다는 말이다)
  2. 중복된 키가 많은 경우 적합하다.

분포수 세기 알고리즘은 기수 정렬에서 사용되어 강력한 기능을 발휘하므로 눈여겨 볼 필요가 있다.

문서에 대하여